Skip to content

Add PipeWire audio fallback for modern Linux distributions#39

Merged
infinityabundance merged 4 commits intomainfrom
copilot/add-pipewire-audio-fallback
Feb 13, 2026
Merged

Add PipeWire audio fallback for modern Linux distributions#39
infinityabundance merged 4 commits intomainfrom
copilot/add-pipewire-audio-fallback

Conversation

Copy link
Contributor

Copilot AI commented Feb 13, 2026

Summary

Adds PipeWire audio backend between PulseAudio and Dummy fallbacks. Modern distributions (Fedora 40+, Ubuntu 24.04+, Arch) use PipeWire by default and may lack working ALSA/PulseAudio backends.

Details

  • New feature

What changed?

Fallback chain: ALSA → PulseAudio → PipeWire → Dummy

New files:

  • src/audio_capture_pipewire.c - Capture via pw_stream (48kHz stereo, 5ms frames)
  • src/audio_playback_pipewire.c - Playback via pw_stream

Modified files:

  • include/rootstream.h - Added 8 PipeWire function signatures
  • src/service.c - Inserted PipeWire into capture/playback backend arrays
  • CMakeLists.txt - Detection via pkg-config (libpipewire-0.3), conditional compilation via HAVE_PIPEWIRE
  • src/diagnostics.c - Reports PipeWire status, fixed HAVE_PULSEHAVE_PULSEAUDIO macro

Implementation details:

  • Availability checks test actual daemon connectivity before selection
  • Balanced pw_init()/pw_deinit() calls prevent resource leaks
  • Non-blocking operation with internal buffering (4-frame ring buffer)
  • Debug logging for initial buffering phase

Rationale

PipeWire replaces both ALSA and PulseAudio on modern distributions. Without this fallback, audio streaming fails entirely on PipeWire-only systems despite functioning audio subsystem.

Aligns with RootStream's Linux-native focus by supporting current audio infrastructure while maintaining compatibility with legacy systems through existing fallbacks.

Testing

  • Built successfully with PipeWire detection
  • Graceful fallback when PipeWire unavailable (no build/runtime errors)
  • CodeQL security scan passed (no vulnerabilities)

Notes

  • Latency: Matches existing 5ms frame timing, no impact
  • Resource usage: PipeWire backend only instantiated if selected, minimal overhead
  • Build requirements: Optional libpipewire-0.3-dev (apt), pipewire-devel (dnf), or pipewire (pacman)
  • Follow-up: None required, fully operational
Original prompt

PHASE 7: PipeWire Audio Fallback for Capture & Playback

Current State

  • ✅ PHASE 3: ALSA audio (primary)
  • ✅ PHASE 3: PulseAudio audio (fallback 1)
  • ✅ PHASE 3: Dummy audio (fallback 2)
  • Missing: PipeWire audio (modern Linux default)

Problem

On modern distributions (Fedora 40+, Ubuntu 24.04+, Arch):

  • PipeWire is the new default audio/video server
  • Replaces both PulseAudio and ALSA in many cases
  • Systems may have ONLY PipeWire available
  • ALSA and PulseAudio backends may not work at all
  • Audio streaming fails even though PipeWire is available

Solution: Add PipeWire as Tier-2 Fallback

Current fallback chain:

ALSA → PulseAudio → Dummy

After Phase 7:

ALSA → PulseAudio → PipeWire → Dummy

This ensures audio works on:

  • Traditional ALSA systems (legacy)
  • PulseAudio systems (common)
  • PipeWire-only systems (modern) ← NEW
  • Headless/test systems (dummy)

Implementation

File 1: src/audio_capture_pipewire.c - PipeWire Audio Capture

/*
 * audio_capture_pipewire.c - PipeWire audio capture fallback
 * 
 * Works on modern Linux distributions where PipeWire is the default audio server.
 * Fedora 40+, Ubuntu 24.04+, Arch, etc.
 * 
 * Uses pw_stream for simple, non-blocking audio capture.
 */

#include "../include/rootstream.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>

#ifdef HAVE_PIPEWIRE
#include <pipewire/pipewire.h>
#include <pipewire/type.h>
#include <spa/param/audio/format.h>
#include <spa/param/props.h>
#include <spa/utils/result.h>

typedef struct {
    struct pw_loop *loop;
    struct pw_stream *stream;
    struct pw_core *core;
    struct pw_context *context;
    
    int16_t *buffer;
    size_t buffer_size;
    size_t read_pos;
    
    int sample_rate;
    int channels;
    int frame_size;
} pipewire_capture_ctx_t;

/* Stream events callback */
static void on_process(void *userdata) {
    pipewire_capture_ctx_t *pw = (pipewire_capture_ctx_t *)userdata;
    struct pw_buffer *b;
    struct spa_buffer *buf;

    if ((b = pw_stream_dequeue_buffer(pw->stream)) == NULL) {
        return;
    }

    buf = b->buffer;
    
    /* Get audio data from buffer */
    for (int i = 0; i < buf->n_datas; i++) {
        struct spa_data *d = &buf->datas[i];
        
        if (d->data == NULL) continue;
        
        uint32_t n_samples = d->chunk->size / sizeof(int16_t);
        int16_t *samples = (int16_t *)d->data;
        
        /* Copy to our buffer */
        if (pw->read_pos + n_samples <= pw->buffer_size) {
            memcpy(pw->buffer + pw->read_pos, samples, 
                   n_samples * sizeof(int16_t));
            pw->read_pos += n_samples;
        }
    }

    pw_stream_queue_buffer(pw->stream, b);
}

static const struct pw_stream_events stream_events = {
    PW_VERSION_STREAM_EVENTS,
    .process = on_process,
};

/*
 * Initialize PipeWire audio capture
 */
int audio_capture_init_pipewire(rootstream_ctx_t *ctx) {
    pipewire_capture_ctx_t *pw = calloc(1, sizeof(pipewire_capture_ctx_t));
    if (!pw) {
        fprintf(stderr, "ERROR: Memory allocation failed\n");
        return -1;
    }

    pw->sample_rate = 48000;
    pw->channels = 2;
    pw->frame_size = 240;  /* 5ms at 48kHz */
    pw->buffer_size = pw->frame_size * pw->channels * 4;  /* 4 frames buffer */

    pw->buffer = calloc(pw->buffer_size, sizeof(int16_t));
    if (!pw->buffer) {
        free(pw);
        return -1;
    }

    pw->read_pos = 0;

    /* Initialize PipeWire */
    pw_init(NULL, NULL);

    /* Create main loop */
    pw->loop = pw_loop_new(NULL);
    if (!pw->loop) {
        fprintf(stderr, "ERROR: Cannot create PipeWire main loop\n");
        free(pw->buffer);
        free(pw);
        return -1;
    }

    /* Create context */
    pw->context = pw_context_new(pw->loop, NULL, 0);
    if (!pw->context) {
        fprintf(stderr, "ERROR: Cannot create PipeWire context\n");
        pw_loop_destroy(pw->loop);
        free(pw->buffer);
        free(pw);
        return -1;
    }

    /* Create core and connect */
    pw->core = pw_context_connect(pw->context, NULL, 0);
    if (!pw->core) {
        fprintf(stderr, "ERROR: Cannot connect to PipeWire core\n");
        pw_context_destroy(pw->context);
        pw_loop_destroy(pw->loop);
        free(pw->buffer);
        free(pw);
        return -1;
    }

    /* Create stream */
    pw->stream = pw_stream_new_simple(
        pw->loop,
        "RootStream Capture",
        pw_properties_new(
            PW_KEY_MEDIA_TYPE, "Audio",
            PW_KEY_MEDIA_CATEGORY, "Capture",
            PW_KEY_AUDIO_FORMAT, "S16LE",
            NULL
        ),
        &stream_events,
        pw
    );

    if (!pw->stream) {
        fprintf(stderr, "ERROR: Cannot create PipeWire stream\n");
        pw_core_disconnect(pw->core);
        pw_context_destroy(pw->context);
        pw_loop_destroy(pw->loop);
        free(pw->buffer);
        free(pw);
        return -1;
   ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 3 commits February 13, 2026 03:01
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
…e ambiguous buffers

Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Copilot AI changed the title [WIP] Add PipeWire audio as fallback for capture and playback Add PipeWire audio fallback for modern Linux distributions Feb 13, 2026
@infinityabundance infinityabundance marked this pull request as ready for review February 13, 2026 03:07
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@infinityabundance infinityabundance merged commit 1986442 into main Feb 13, 2026
4 of 9 checks passed
@infinityabundance infinityabundance deleted the copilot/add-pipewire-audio-fallback branch February 19, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants